package hos.http;

import android.os.Build;
import android.text.TextUtils;
import android.util.Base64;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

/**
 * <p>Title: HttpFileUtils </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2024-03-29 9:48
 */
public class HttpFileUtils {


    public static String md5String(String target) {
        return md5String(target, false);
    }

    public static String md5String(String target, boolean base64) {
        if (target == null || target.isEmpty()) {
            return "";
        }
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                md.update(target.getBytes(StandardCharsets.UTF_8));
            } else {
                md.update(target.getBytes(Charset.forName("UTF-8")));
            }
            byte[] md5 = md.digest();
            if (base64) {
                byte[] decode = Base64.decode(md5, Base64.DEFAULT);
                return byteToString(decode);
            }
            return byteToString(md5);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String md5(File file) {
        return md5(file, false);
    }

    public static String md5(File file, boolean base64) {
        if (file == null) {
            return "";
        }
        FileInputStream fileInputStream = null;
        DigestInputStream digestInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            digestInputStream = new DigestInputStream(fileInputStream, MessageDigest.getInstance("MD5"));
            //            1024 * 256
            byte[] buffer = new byte[1024 * 256];
            try {
                while (true) {
                    if (digestInputStream.read(buffer) <= 0) break;
                }
            } finally {
                CloseableUtils.closeFinally(digestInputStream);
            }
            byte[] md5 = digestInputStream.getMessageDigest().digest();
            if (base64) {
                byte[] decode = Base64.decode(md5, Base64.DEFAULT);
                return byteToString(decode);
            }
            return byteToString(md5);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            CloseableUtils.closeFinally(digestInputStream, fileInputStream);
        }
        return "";
    }

    private static String byteToString(byte[] md5) {
        StringBuilder sb = new StringBuilder();
        int d;
        for (byte value : md5) {
            d = value;
            if (d < 0) {
                d = value & 0xff;
                // 与上一行效果等同
                // i += 256;
            }
            if (d < 16) sb.append("0");
            sb.append(Integer.toHexString(d));
        }
        return sb.toString();
    }

    /**
     * 按照以下顺序返回最终的下载文件的名称
     * <p>
     * 1. 指定文件名
     * 2. 响应头文件名
     * 3. 请求URL路径
     * 4. 时间戳
     */
    public static String getFileName(String url, String headerDisposition, HttpTag.DownloadListeners progressListeners) {
        String fileName = null;
        boolean isFileNameDecode = true;
        if (progressListeners != null) {
            fileName = progressListeners.fileName;
            isFileNameDecode = progressListeners.isFileNameDecode;
        }
        if (fileName != null && !fileName.isEmpty()) {
            return fileName;
        }
        if (headerDisposition != null) {
            String fileNameDisposition = HttpStringUtils.substringAfter(headerDisposition, "filename=", "");
            if (fileNameDisposition != null && !fileNameDisposition.isEmpty()) {
                fileNameDisposition = fileNameDisposition.trim();
            }
            if (fileNameDisposition != null && !fileNameDisposition.isEmpty()) {
                return fileNameDisposition;
            }
            String fileNameDispositionMore = HttpStringUtils.substringAfter(headerDisposition, "filename*=", "");
            if (fileNameDispositionMore != null && !fileNameDispositionMore.isEmpty()) {
                fileNameDispositionMore = fileNameDispositionMore.trim();
            }
            if (fileNameDispositionMore != null && !fileNameDispositionMore.isEmpty()) {
                return fileNameDispositionMore;
            }
        }
        fileName = HttpStringUtils.substringAfterLast(url, '/', "");
        if (fileName == null || fileName.isEmpty()) {
            fileName = "unknown_" + System.currentTimeMillis();
        } else if (isFileNameDecode) {
            try {
                return URLDecoder.decode(fileName, "UTF8");
            } catch (UnsupportedEncodingException e) {
                return fileName;
            }
        }
        return fileName;
    }

    public static File rename(String dir, String fileNameWithoutExtension, String fileExtension, int index) {
        File file = new File(dir, fileNameWithoutExtension + "_(" + index + ")" + fileExtension);
        if (file.exists()) {
            rename(dir, fileNameWithoutExtension, fileExtension, index + 1);
        }
        return file;
    }

    /**
     * 解析下载文件
     *
     * @param url               访问路径
     * @param md5Header         文件的md5
     * @param progressListeners 进度监听
     */
    public static File parseFile(
            String url, String headerDisposition, InputStream stream, String md5Header,
            HttpTag.DownloadListeners progressListeners
    ) throws HttpException {
        String dir = null;
        String fileName;
        boolean isTempFile = false;
        boolean isMD5Verify = false;
        if (progressListeners != null) {
            dir = progressListeners.fileDir;
            isTempFile = progressListeners.isTempFile;
            isMD5Verify = progressListeners.isMD5Verify;
        }
        if (dir == null || dir.length() == 0) {
            dir = HttpConfig.config().getContext().getFilesDir().getAbsolutePath();
        }
        File dirFile = new File(dir);
        // 判断downloadDir是否为目录
        File file;
        if (dirFile.isDirectory()) {
            fileName = getFileName(url, headerDisposition, progressListeners);
            file = new File(dir, fileName);
        } else {
            final String temp = dir;
            dir = HttpStringUtils.substringBeforeLast(dir, File.separatorChar);
            fileName = HttpStringUtils.substringAfterLast(temp, File.separatorChar);
            file = dirFile;
        }
        try {
            if (file.exists()) {
                // 文件存在
                // MD5校验匹配文件
                if (isMD5Verify) {
                    if (md5Header != null && TextUtils.equals(md5(file, true), md5Header)) {
                        // 进度直接结束
                        if (!progressListeners.isEmpty()) {
                            Progress progress = new Progress();
                            long fileSize = file.length();
                            progress.currentByteCount = fileSize;
                            progress.totalByteCount = fileSize;
                            progress.intervalByteCount = fileSize;
                            progress.finish = true;
                            for (ProgressListener progressListener : progressListeners) {
                                progressListener.onProgress(progress);
                            }
                        }
                        return file;
                    }
                }
                // 命名冲突，删除之前的，重新下载
                if (progressListeners.conflictRename && TextUtils.equals(file.getName(), fileName)) {
                    String fileExtension = HttpStringUtils.substringAfterLast(file.getName(), '.', "");
                    String fileNameWithoutExtension = HttpStringUtils.substringBeforeLast(file.getName(), '.');
                    file = HttpFileUtils.rename(dir, fileNameWithoutExtension, fileExtension, 1);
                }
            }
            if (isTempFile) {
                file = new File(dir, file.getName() + ".http-download");
            }
            // 写入文件
            if (stream == null) throw HttpException.throwDownload("stream is null");
            if (!writeFileFromIS(file, stream, false, null)) {
                throw HttpException.throwDownload("writeFileFromIS error");
            }
            // 下载完毕删除临时文件
            if (isTempFile) {
                File fileFinal = new File(dir, fileName);
                file.renameTo(fileFinal);
                return fileFinal;
            }
            return file;
        } catch (IOException e) {
            if (isTempFile) {
                file.delete();
            }
            throw HttpException.throwCancel(url, e);
        } catch (Exception e) {
            throw HttpException.throwDownload(url, e);
        }
    }

    public static boolean writeFileFromIS(
            File file,
            InputStream stream,
            Boolean append,
            ProgressListener listener) {
        int bufferSize = 8192;
        OutputStream os = null;
        try {
            os = new BufferedOutputStream(
                    new FileOutputStream(file, append),
                    bufferSize
            );
            if (listener == null) {
                byte[] data = new byte[bufferSize];
                int len;
                while ((len = stream.read(data)) != -1) {
                    os.write(data, 0, len);
                }
            } else {
                int totalSize = stream.available();
                long currentByteCount = 0;
                Progress progress = new Progress();
                listener.onProgress(progress);
                byte[] data = new byte[bufferSize];
                int bytesRead;
                while ((bytesRead = stream.read(data)) != -1) {
                    os.write(data, 0, bytesRead);
                    currentByteCount += bytesRead;
                    HttpUtils.progressListener(listener, progress, totalSize, currentByteCount, bytesRead);
                }
            }
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            CloseableUtils.closeFinally(stream, os);
        }
    }

}
